<?php

namespace app\models;

use yii\base\InvalidConfigException;
use yii\base\Model;

/**
 * 缓存
 * Class CacheModel
 * @package app\helper
 */
class CacheModel extends Model
{

    /**
     * 实例
     * @var
     */
    private static $instance;
    /**
     * 缓存列表
     *  ` 对应[config]的配置[key]不同请自行[value] `
     * @var array
     */
    private static $cacheTypeList = [
        'fastCache' => 'fastCache', // 快速缓存，一般对应[MemeCache]、[MemeCached]
        'fileCache' => 'cache', // YII2自带的[文件缓存]
        'redis' => 'redis' // [redis]缓存
    ];
    /**
     * 缓存[KEY]列表
     * @var array
     */
    private static $keyList = [
        'backendMenu' => 'backendMenu', // 后台菜单
    ];
    /**
     * 缓存秘钥
     *  ` 用于加在缓存[KEY]前 `
     * @var string
     */
    private $secretKey = 'rRpwFtF0wXsaH9AWwriO_';
    /**
     * 旧数据
     * @var array
     */
    private $oldValue = [];
    /**
     * 缓存类型
     * @var
     */
    public $cacheType;
    /**
     * 缓存[KEY]
     *  ` 可以初始化时候赋值，也可以在[set, get]方法上传输 `
     * @var
     */
    public $cacheKey;
    /**
     * 缓存[VALUE]
     *  ` 可以初始化时候赋值，也可以在[set]方法上传输 `
     * @var
     */
    public $cacheValue;

    /**
     * 验证规则
     * @return array
     */
    public function rules()
    {

        $cacheTypeList = array_values(self::getCacheTypeList());
        return [
            [['cacheType'], 'required'],
            [['cacheKey', 'cacheValue'], 'string'],
            ['cacheType', 'in', 'range' => $cacheTypeList, 'message' => '缓存类型不存在']
        ];
    }

    /**
     * 标签
     * @return array
     */
    public function attributeLabels()
    {

        return [];
    }

    /**
     * 初始化
     * @param $config
     * @return CacheModel
     */
    public static function loadModel($config)
    {

        return self::getInstance()->initInstance($config);
    }

    /**
     * 获取实例
     * @return CacheModel
     */
    protected static function getInstance()
    {

        if (empty(self::$instance))
            self::$instance = new self();

        return self::$instance;
    }

    /**
     * 初始化一些默认信息
     * @param string $config
     * @return CacheModel
     */
    protected function initInstance($config = '')
    {

        $instance = self::getInstance();

        // 加载配置
        $this->loadConfig($config);
        return $instance;
    }

    /**
     * 载入某些配置
     * @param $config
     * @return CacheModel
     */
    public function loadConfig($config)
    {

        $instance = self::getInstance();

        foreach ($config as $k => $v) {

            if (!$instance->hasAttribute($k))
                continue;

            $instance->setAttributes([
                $k => $v
            ], false);
        }

        return $instance;
    }

    /**
     * 更新缓存中某值
     * @param string $cacheKey
     * @param string $cacheValue
     * @return bool
     */
    public function set($cacheKey = '', $cacheValue = '')
    {


        // 缓存[KEY]
        $cacheKey = $this->getFormatKey($cacheKey);
        // 缓存[VALUE]
        $cacheValue = $this->getFormatValue($cacheValue);

        // 验证
        if ($this->hasErrors() || !$this->validate()) return false;

        try {

            $result = \Yii::$app->get($this->cacheType, false);
            if (empty($result)) {
                $this->addError('cacheType', '缓存类型未配置，请在配置文件中配置缓存类型');
                return false;
            }

            return $result->set($cacheKey, $cacheValue);

        } catch (InvalidConfigException $error) {

            // 记录下错误日志
            \Yii::error([

                "````````````````````````````````````````````````````````",
                "``                      缓存错误                        ``",
                "`` 错误详情: 缓存[{$cacheKey}] - [$cacheValue]写入失败    ``",
                "``          {$error->getMessage()}                    ``",
                "`` 错误信息和参数详情:                                   ``",
                "````````````````````````````````````````````````````````",
                $error->getTraceAsString()
            ], 'normal');

            $this->addError('cacheKey', '缓存写入失败，请检查日志');
            return false;
        }
    }

    /**
     * 获取缓存中的某值
     * @param string $cacheKey
     * @return bool
     */
    public function get($cacheKey = '')
    {

        // 缓存[KEY]
        $cacheKey = $this->getFormatKey($cacheKey);

        // 验证
        if ($this->hasErrors() || !$this->validate()) return false;

        try {

            $result = \Yii::$app->get($this->cacheType, false);
            if (empty($result)) {
                $this->addError('cacheType', '缓存类型未配置，请在配置文件中配置缓存类型');
                return false;
            }

            // 获取值
            $oldValue = $result->get($cacheKey);
            // 将[JSON]转为数组
            $this->oldValue = json_decode($oldValue, true);

            return $this->oldValue['value'];

        } catch (InvalidConfigException $error) {

            // 记录下错误日志
            \Yii::error([

                "````````````````````````````````````````````````````````",
                "``                      缓存错误                        ``",
                "`` 错误详情: 缓存[{$cacheKey}]读取失败                    ``",
                "``          {$error->getMessage()}                    ``",
                "`` 错误信息和参数详情:                                   ``",
                "````````````````````````````````````````````````````````",
                $error->getTraceAsString()
            ], 'normal');

            $this->addError('cacheKey', '缓存读取失败，请检查日志');
            return false;
        }
    }

    /**
     * 是否数据已经过期
     * @param int $afterSecond 检测秒数
     *  ` 距离数据存储时间经过多少秒算过期 `
     * @return bool
     */
    public function isExpired($afterSecond = 0)
    {

        // 过期时间为空或者[-1]则 永不过期
        if (empty($this->oldValue['update_time']) || $this->oldValue['update_time'] = -1) return false;

        $nowTime = time();
        // 过期时间大于[检测秒数]
        if ($nowTime - $this->oldValue['update_time'] >= $afterSecond) return true;

        return false;
    }

    /**
     *  返回是否class存在某个属性
     * @param string $name the name of the attribute
     * @return bool whether the model has an attribute with the specified name.
     */
    protected function hasAttribute($name)
    {
        return isset($this->attributes[$name]) || in_array($name, $this->attributes(), true);
    }

    /**
     * 获取格式化缓存[KEY]
     * @param $cacheKey
     * @return string
     */
    protected function getFormatKey($cacheKey)
    {

        // 缓存[KEY]获取
        $cacheKey = !empty($cacheKey) ? $cacheKey : $this->cacheKey;

        // 已经添加了则 直接返回
        if (strstr($this->secretKey, $cacheKey)) return $cacheKey;

        // 验证[KEY]不为空
        if (empty($cacheKey)) {

            $this->addError('cacheKey', '缓存写入失败，缓存键值不能为空');
            return false;
        }

        // 基本就是在前端添加[秘钥头],,,防止多项目混乱
        $cacheKey = $this->secretKey . $cacheKey;
        return $cacheKey;
    }

    /**
     * 获取格式化缓存[VALUE]
     * @param $cacheValue
     * @return bool
     */
    protected function getFormatValue($cacheValue)
    {

        $cacheValue = !empty($cacheValue) ? $cacheValue : $this->cacheValue;
        // 验证内容不为空
        if (empty($cacheValue)) {
            $this->addError('cacheValue', '缓存写入失败，缓存内容不能为空');
            return false;
        }

        $cacheValue = [
            'update_time' => time(),
            'value' => $cacheValue
        ];
        // 数组转为[JSON]
        $cacheValue = json_encode($cacheValue, JSON_UNESCAPED_UNICODE);

        return $cacheValue;
    }

    /**
     * 生成一个N位随机字符
     * @param int $len
     * @return string
     */
    public static function getRandomStr($len = 24)
    {
        $chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
        $final = '';
        for ($i = 0; $i < $len; $i++) {

            $final .= $chars[mt_rand(0, strlen($chars) - 1)];
        }
        return $final;
    }

    /**
     * 获取[缓存类型]列表
     * @return array
     */
    private static function getCacheTypeList()
    {
        return self::$cacheTypeList;
    }

    /**
     * 获取缓存类型[快速缓存]
     * @return bool|mixed
     */
    public static function getTypeFastCache()
    {

        if (empty(self::$cacheTypeList['fastCache'])) return false;
        return self::$cacheTypeList['fastCache'];
    }

    /**
     * 获取缓存类型[文件缓存缓存]
     * @return bool|mixed
     */
    public static function getTypeFileCache()
    {

        if (empty(self::$cacheTypeList['fileCache'])) return false;
        return self::$cacheTypeList['fileCache'];
    }

    /**
     * 获取缓存类型[redis]
     * @return bool|mixed
     */
    public static function getTypeRedis()
    {

        if (empty(self::$cacheTypeList['redis'])) return false;
        return self::$cacheTypeList['redis'];
    }

    /**
     * 获取[后台菜单]键值
     */
    public static function getKeyBackendMenu()
    {
        return self::$keyList['backendMenu'];
    }

}
